﻿/****************************************************************************************************
	Copyright (C) 2010 RapidWebDev Organization (http://rapidwebdev.org)
	Author: Eunge, Legal Name: Jian Liu, Email: eunge.liu@RapidWebDev.org

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ****************************************************************************************************/

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using BaoJianSoft.Common;
using BaoJianSoft.Common.Web;
using BaoJianSoft.RapidWeb.Controls;
using BaoJianSoft.RapidWeb.DynamicPages.Configurations;
using BaoJianSoft.RapidWeb.Properties;
using BaoJianSoft.RapidWeb.WebResources;
using Newtonsoft.Json;

namespace BaoJianSoft.RapidWeb.Services
{
	/// <summary>
	/// Frame page handler. 
	/// Frame page - when the user log onto system, the page is composed of navigation, page header, page footer and business/content page. We call this page as Frame page. It's the main entry of the system.
	/// </summary>
	public class FramePageHandler : SupportDocumentReadyPage, IRequiresSessionState
	{
		private const string NOCACHE_META = @"
				<meta http-equiv=""Expires"" content=""0"" ></meta>
				<meta http-equiv=""Cache-Control"" content=""no-cache""></meta>
				<meta http-equiv=""Pragma"" content=""no-cache""></meta>";

		private IApplicationContext applicationContext;
		private IPermissionBridge permissionBridge;
		private FramePageConfigurationSection configurationSection;
		private List<KeyValuePair<Panel, ISiteMapItem>> navigationPanels = new List<KeyValuePair<Panel, ISiteMapItem>>();
		private Panel panelHeader;
		private Panel panelFooter;

		/// <summary>
		/// Raises the System.Web.UI.Control.Init event to initialize the page.
		/// </summary>
		/// <param name="e">An System.EventArgs that contains the event data.</param>
		protected override void OnInit(EventArgs e)
		{
			base.OnInit(e);

			this.Initialize();

			HtmlGenericControl htmlTag = new HtmlGenericControl("html");
			htmlTag.Attributes["xmlns"] = "http://www.w3.org/1999/xhtml";
			this.Controls.Add(htmlTag);

			this.CreateHtmlHead(htmlTag);
			this.CreateHtmlBody(htmlTag);
		}

		/// <summary>
		/// Raises the System.Web.UI.Control.PreRender event.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);

			string registeredJs = "window.FramePageObj = new FramePageClass({0}, {1});";
			registeredJs = string.Format(CultureInfo.InvariantCulture, registeredJs, this.CreateRegionJsonString(), this.CreateOptionsJsonString());
			ClientScripts.RegisterStartupScript(registeredJs);
		}

		/// <summary>
		/// Initializes the System.Web.UI.HtmlTextWriter object and calls on the child controls of the System.Web.UI.Page to render.
		/// </summary>
		/// <param name="writer"></param>
		protected override void Render(HtmlTextWriter writer)
		{
			writer.Write(@"<!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.0 Transitional//EN"" ""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"">");
			base.Render(writer);
		}

		/// <summary>
		/// The method is working on validating the permission and authentication of user to the page.
		/// </summary>
		private void Initialize()
		{
			this.configurationSection = ConfigurationManager.GetSection("framePage") as FramePageConfigurationSection;

			this.applicationContext = SpringContext.Current.GetObject<IApplicationContext>();
			this.permissionBridge = SpringContext.Current.GetObject<IPermissionBridge>();
			if (!this.permissionBridge.HasPermission(this.configurationSection.PermissionValue))
			{
				WebUtility.RedirectToPage(WebUtility.NotAuthorizedUrl);
				return;
			}

			if (this.configurationSection == null)
			{
				Logger.Instance(this).Error(@"The configuration section ""framePage"" is not found in web.config.");
				WebUtility.RedirectToPage(WebUtility.InternalServerError);
			}
		}

		private void CreateHtmlHead(Control htmlContainer)
		{
			htmlContainer.Controls.Add(new HtmlHead());
			this.Header.Controls.Add(new HtmlTitle { Text = this.configurationSection.FramePageTitle ?? Resources.FP_DefaultPageTitle });

			LiteralControl metaDefinition = new LiteralControl(NOCACHE_META);
			this.Header.Controls.Add(metaDefinition);

			// register style and script file references.
			IWebResourceManager resourceManager = SpringContext.Current.GetObject<IWebResourceManager>();
			resourceManager.Flush("WebResources.FramePage");
		}

		private void CreateHtmlBody(Control htmlContainer)
		{
			HtmlGenericControl htmlBody = new HtmlGenericControl("body");
			htmlBody.Attributes["class"] = "framepagebody";
			htmlContainer.Controls.Add(htmlBody);

			HtmlForm htmlForm = new HtmlForm { ID = "FramePageForm" };
			htmlBody.Controls.Add(htmlForm);

			Panel pageContainer = new Panel { ID = "FramePageContainer", CssClass = "framepagecontainer" };
			htmlForm.Controls.Add(pageContainer);

			this.panelHeader = new Panel { ID = "Header" };
			pageContainer.Controls.Add(this.panelHeader);
			if (!string.IsNullOrEmpty(this.configurationSection.HeaderTemplate))
			{
				Control headerControl = this.LoadControl(this.configurationSection.HeaderTemplate);
				this.panelHeader.Controls.Add(headerControl);
			}

			this.CreateNavigationPanels(pageContainer);

			this.panelFooter = new Panel { ID = "Footer" };
			pageContainer.Controls.Add(this.panelFooter);
			if (!string.IsNullOrEmpty(this.configurationSection.FooterTemplate))
			{
				Control footerControl = this.LoadControl(this.configurationSection.FooterTemplate);
				this.panelFooter.Controls.Add(footerControl);
			}
		}

		private void CreateNavigationPanels(Panel pageContainer)
		{
			IEnumerable<ISiteMapItem> siteMapItemConfigEnumerable = this.permissionBridge.ResolveSiteMapItems();
			foreach (ISiteMapItem siteMapItemConfig in siteMapItemConfigEnumerable)
			{
				Panel panelNavigationSegment = new Panel { ID = siteMapItemConfig.Id, CssClass = "navigation" };
				pageContainer.Controls.Add(panelNavigationSegment);
				this.navigationPanels.Add(new KeyValuePair<Panel, ISiteMapItem>(panelNavigationSegment, siteMapItemConfig));
				CreateNavigationSubPanels(panelNavigationSegment, siteMapItemConfig.Children, 1);
			}
		}

		private static void CreateNavigationSubPanels(Panel navigationSegment, IEnumerable<ISiteMapItem> siteMapItemConfigEnumerable, int hierarchyLevel)
		{
			const string parentNavigationLiteralTemplate = "<div class='nav$HierarchyLevel$'><span class='parent'>$NavigationItemTitle$</span></div>";
			const string leafNavigationLiteralTemplate = @"<div class='nav$HierarchyLevel$'><a href=""javascript:$JavaScriptBlock$"">$NavigationItemTitle$</a></div>";
			const string separatorLiteralTemplate = @"<div class=""separator""></div>";

			if (siteMapItemConfigEnumerable == null) return;

			foreach (ISiteMapItem siteMapItemConfig in siteMapItemConfigEnumerable)
			{
				string html = null;
				bool hasChildren = siteMapItemConfig.Children != null && siteMapItemConfig.Children.Count() > 0;
				if (siteMapItemConfig.Type != SiteMapItemTypes.Separator && !hasChildren)
				{
					string js = null;
					if (string.IsNullOrEmpty(siteMapItemConfig.ClientSideCommand))
						js = "window.FramePageObj.AddTab({ Id: '$NavigationItemId$', Url: '$NavigationItemUrl$', Title: '$NavigationItemTitle$' })"
							.Replace("$NavigationItemId$", siteMapItemConfig.Id)
							.Replace("$NavigationItemUrl$", ControlJavaScriptGenerator.EncodeJavaScriptStringWithSingleQuote(siteMapItemConfig.PageUrl))
							.Replace("$NavigationItemTitle$", ControlJavaScriptGenerator.EncodeJavaScriptStringWithSingleQuote(siteMapItemConfig.Text));
					else
						js = siteMapItemConfig.ClientSideCommand;

					html = leafNavigationLiteralTemplate.Replace("$HierarchyLevel$", hierarchyLevel.ToString())
						.Replace("$JavaScriptBlock$", js)
						.Replace("$NavigationItemTitle$", ControlJavaScriptGenerator.EncodeJavaScriptStringWithSingleQuote(siteMapItemConfig.Text));
				}
				else if (siteMapItemConfig.Type != SiteMapItemTypes.Separator && hasChildren)
				{
					html = parentNavigationLiteralTemplate.Replace("$HierarchyLevel$", hierarchyLevel.ToString())
						.Replace("$NavigationItemTitle$", ControlJavaScriptGenerator.EncodeJavaScriptStringWithSingleQuote(siteMapItemConfig.Text));
				}
				else if (siteMapItemConfig.Type == SiteMapItemTypes.Separator)
					html = separatorLiteralTemplate;

				LiteralControl literalControl = new LiteralControl(html);
				navigationSegment.Controls.Add(literalControl);
				if (hasChildren)
					CreateNavigationSubPanels(navigationSegment, siteMapItemConfig.Children, hierarchyLevel + 1);
			}
		}

		private string CreateRegionJsonString()
		{
			StringBuilder regionJsonBuilder = new StringBuilder();
			using (StringWriter stringWriter = new StringWriter(regionJsonBuilder))
			using (JsonTextWriter jsonTextWriter = new JsonTextWriter(stringWriter))
			{
				jsonTextWriter.WriteStartObject();

				jsonTextWriter.WritePropertyName("HeaderPanelId");
				jsonTextWriter.WriteValue(this.panelHeader.ClientID);

				jsonTextWriter.WritePropertyName("FooterPanelId");
				jsonTextWriter.WriteValue(this.panelFooter.ClientID);

				jsonTextWriter.WritePropertyName("NavigationBarTitle");
				jsonTextWriter.WriteValue(this.configurationSection.NavigationBarTitle);

				jsonTextWriter.WritePropertyName("NavigationItems");
				jsonTextWriter.WriteStartArray();
				foreach (KeyValuePair<Panel, ISiteMapItem> kvp in this.navigationPanels)
				{
					jsonTextWriter.WriteStartObject();
					jsonTextWriter.WritePropertyName("Id");
					jsonTextWriter.WriteValue(kvp.Key.ClientID);

					jsonTextWriter.WritePropertyName("Title");
					jsonTextWriter.WriteValue(kvp.Value.Text);

					jsonTextWriter.WritePropertyName("IconClass");
					jsonTextWriter.WriteValue(kvp.Value.IconClassName);

					jsonTextWriter.WriteEndObject();
				}

				jsonTextWriter.WriteEndArray();
				jsonTextWriter.WriteEndObject();
			}

			return regionJsonBuilder.ToString();
		}

		private string CreateOptionsJsonString()
		{
			//configurationSection
			StringBuilder regionJsonBuilder = new StringBuilder();
			using (StringWriter stringWriter = new StringWriter(regionJsonBuilder))
			using (JsonTextWriter jsonTextWriter = new JsonTextWriter(stringWriter))
			{
				jsonTextWriter.WriteStartObject();

				jsonTextWriter.WritePropertyName("EnableMultipleTabs");
				jsonTextWriter.WriteValue(this.configurationSection.EnableMultipleTabs);

				jsonTextWriter.WritePropertyName("DefaultTab");
				jsonTextWriter.WriteStartObject();
				jsonTextWriter.WritePropertyName("Url");
				jsonTextWriter.WriteValue(this.configurationSection.DefaultPageUrl);
				jsonTextWriter.WritePropertyName("Title");
				jsonTextWriter.WriteValue(this.configurationSection.DefaultTabTitle);
				jsonTextWriter.WritePropertyName("Id");
				jsonTextWriter.WriteValue("DefaultTabAutoAssignedId");
				jsonTextWriter.WriteEndObject();

				jsonTextWriter.WriteEndObject();
			}

			return regionJsonBuilder.ToString();
		}
	}
}
